#include <Arduino.h>
#include <WebServer.h>
#include "web_server.h"
#include "config.h"
#include "plog.h"
#include "ptime.h"
#include "pwifi.h"
#include "persistence.h"
#include <vector>
#include <algorithm>
#include <esp_heap_caps.h>
#include <CRC32.h>

#ifdef WEB_SERVER_ASYNC
AsyncWebServer server(WEB_SERVER_PORT);
#else
WebServer server(WEB_SERVER_PORT);
#endif

bool _started = false;
void web_server_start(void)
{
  if (_started)
    return;
  pwifi_ap_mode_init();
  delay(500); // 必须的等待
  web_server_init();
  _started = true;
}

void web_server_stop(void)
{
  if (!_started)
    return;
#ifdef WEB_SERVER_ASYNC
  server.end();
#else
  server.close();
#endif
  LOG_INFO("Web server stopped");
  delay(500); // 必须的等待
  pwifi_ap_close();
  _started = false;
}

void web_server_init(void)
{
  // 设置路由
#ifdef WEB_SERVER_ASYNC
  server.on("/", HTTP_GET, handleRoot);
  server.on(WEB_SERVER_URL_DATA_DOWNLOAD, HTTP_GET, handleDownload);
  server.on(WEB_SERVER_URL_DATA_INFO, HTTP_GET, handleDataInfo);
#else
  server.on("/", handleRoot);
  server.on(WEB_SERVER_URL_DATA_DOWNLOAD, handleDownload);
  server.on(WEB_SERVER_URL_DATA_INFO, handleDataInfo);
#endif
  server.begin();
  // todo这里要判断是否需要开启成功

#ifdef WEB_SERVER_ASYNC
  LOG_INFO("Async HTTP server started! port: %d", WEB_SERVER_PORT);
#else
  LOG_INFO("HTTP server started! port: %d", WEB_SERVER_PORT);
#endif
}

void web_server_loop(void)
{
#ifndef WEB_SERVER_ASYNC
  server.handleClient();
#endif
  // AsyncWebServer不需要显式调用handleClient
}

// 处理根路由 - 显示数据概览页面
#ifdef WEB_SERVER_ASYNC
void handleRoot(AsyncWebServerRequest *request)
#else
void handleRoot()
#endif
{
  String html = R"=====(
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>PAqm环境数据监控</title>
  <style>
    * { box-sizing: border-box; font-family: 'Helvetica Neue', Arial, sans-serif; }
    body { margin: 0; padding: 20px; background: #f5f7fa; color: #333; }
    .container { max-width: 800px; margin: 0 auto; }
    .card { background: white; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); padding: 25px; margin-bottom: 25px; }
    h1 { color: #2c3e50; text-align: center; margin-top: 0; }
    .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 15px; margin: 20px 0; }
    .stat-card { text-align: center; padding: 15px; background: #f8f9fa; border-radius: 8px; }
    .stat-value { font-size: 1.8rem; font-weight: 700; color: #3498db; margin: 8px 0; }
    .stat-label { font-size: 0.9rem; color: #7f8c8d; }
    .btn { 
      display: block; width: 100%; padding: 16px; background: #3498db; 
      color: white; text-align: center; text-decoration: none; 
      border-radius: 8px; font-size: 1.1rem; font-weight: 500;
      border: none; cursor: pointer; transition: all 0.2s;
      box-shadow: 0 4px 6px rgba(52, 152, 219, 0.3);
    }
    .btn:hover { background: #2980b9; transform: translateY(-2px); }
    .btn:active { transform: translateY(0); }
    .footer { text-align: center; margin-top: 30px; color: #95a5a6; font-size: 0.9rem; }
    @media (max-width: 480px) {
      .card { padding: 15px; }
      .stat-value { font-size: 1.5rem; }
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="card">
      <h1>PAqm环境监测数据</h1>
      
      <div id="stats" class="stats-grid">
        <!-- 动态数据将通过JavaScript填充 -->
      </div>
      
      <button class="btn" onclick="downloadData()">下载完整数据 (.csv)</button>
    </div>
    
    <div class="footer">
      IP地址: )=====";
  html += WiFi.softAPIP().toString();
  html += R"=====( | 访问时间: <span id="update-time">正在获取...</span>
    </div>
  </div>
  
  <script>
    // 获取数据统计信息
    function fetchDataInfo() {
      fetch(')=====";
  html += WEB_SERVER_URL_DATA_INFO;
  html += R"=====(')
        .then(response => response.json())
        .then(data => {
          document.getElementById('stats').innerHTML = `
            <div class="stat-card">
              <div class="stat-value">${data.count}</div>
              <div class="stat-label">数据总量(条)</div>
            </div>
            <div class="stat-card">
              <div class="stat-value">${data.startDate}</div>
              <div class="stat-label">最早记录</div>
            </div>
            <div class="stat-card">
              <div class="stat-value">${data.endDate}</div>
              <div class="stat-label">最新记录</div>
            </div>
            <div class="stat-card">
              <div class="stat-value">${data.days}</div>
              <div class="stat-label">覆盖天数(天)</div>
            </div>
          `;
          
          document.getElementById('update-time').textContent = new Date().toLocaleString('zh-CN', { 
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit'
          }); // 格式: YYYY-MM-DD HH:mm:ss
        });
    }
    
    // 下载数据
    function downloadData() {
      const btn = event.target;
      btn.textContent = '准备下载中，可能有点慢';
      btn.disabled = true;
      
      // 创建隐藏的iframe触发下载
      const iframe = document.createElement('iframe');
      iframe.style.display = 'none';
      iframe.src = ')=====";
  html += WEB_SERVER_URL_DATA_DOWNLOAD;
  html += R"=====(';
      document.body.appendChild(iframe);
      
      // 5秒后重置按钮
      setTimeout(() => {
        btn.textContent = '下载完整数据 (.csv)';
        btn.disabled = false;
        document.body.removeChild(iframe);
      }, 5000);
    }
    
    // 页面加载时获取数据
    fetchDataInfo();
    
    // 每分钟更新一次数据
    setInterval(fetchDataInfo, 60000);
  </script>
</body>
</html>
)=====";

#ifdef WEB_SERVER_ASYNC
  request->send(200, "text/html", html);
#else
  server.send(200, "text/html", html);
#endif
}
// 提供数据统计信息 (JSON格式)
#ifdef WEB_SERVER_ASYNC
void handleDataInfo(AsyncWebServerRequest *request)
#else
void handleDataInfo()
#endif
{
  Persistence_stats_t stats = P_LittleFS.getStats();

  // 获取时间范围
  String startTime = "无数据";
  String endTime = "无数据";
  int days = 0;

  if (stats.count > 0)
  {
    // Time firstTime = parse_timestamp(stats.first_ts);
    // Time lastTime = parse_timestamp(stats.last_ts);
    // startTime = String(format_ds1302_time_no_wday(&firstTime));
    // endTime = String(format_ds1302_time_no_wday(&lastTime));
    days = (stats.last_ts - stats.first_ts) / (24 * 3600) + 1;
  }

  // 构建JSON响应
  String json = "{";
  json += "\"count\":" + String(stats.count) + ",";
  json += "\"startDate\":\"" + stats.first_ts_raw + "\",";
  json += "\"endDate\":\"" + stats.last_ts_raw + "\",";
  json += "\"days\":" + String(days);
  json += "}";

#if 0
  String json = "{";
  json += "\"count\":" + String(1111) + ",";
  json += "\"startDate\":\"" + String("2023-12-12") + "\",";
  json += "\"endDate\":\"" + String("2023-12-12") + "\",";
  json += "\"days\":" + String(1);
  json += "}";
#endif
#ifdef WEB_SERVER_ASYNC
  request->send(200, "application/json", json);
#else
  server.send(200, "application/json", json);
#endif
}

// deprecated
void traverse_data_callback(uint32_t ts, PersistenceData_t *data, uint32_t count, bool finished)
{
  LOG_DEBUG("count: %d, ts: %d, data: %s, finished: %b", count, ts, Persistence_format(data), finished);

  static String batchBuffer = String("");
  // 构建CSV行
  String csvLine = String(format_ds1302_timestamp_no_wday(ts)) + ",";
  csvLine += String(data->tvoc) + ",";
  csvLine += String(data->eco2) + ",";
  csvLine += String(data->temperature, 2) + ",";
  csvLine += String(data->humidity, 2) + "\n";

  batchBuffer += csvLine;

  // // 每处理1个数据块让出CPU
  // if (count % CHUNK_SIZE == 0 || finished)
  // {
  //   server.sendContent(batchBuffer);
  //   batchBuffer.clear();
  //   yield();
  // }
}
#ifdef WEB_SERVER_ASYNC
void handleDownload(AsyncWebServerRequest *request)
#else
void handleDownload()
#endif
{
  File root = LittleFS.open("/");
  std::vector<String> fileList;

  // 收集文件列表 (保持不变)
  File file = root.openNextFile();
  while (file)
  {
    if (!file.isDirectory() && String(file.name()).endsWith(".csv"))
    {
      fileList.push_back(file.path());
    }
    file.close();
    file = root.openNextFile();
  }
  root.close();

  std::sort(fileList.begin(), fileList.end());

  if (fileList.empty())
  {
#ifdef WEB_SERVER_ASYNC
    request->send(404, "text/plain", "no data");
#else
    server.send(404, "text/plain", "no data");
#endif
    return;
  }
  // 用头尾2个文件的名字拼成zip名
  String zip_file_name = "sensor_data.zip";
  if (fileList.size() == 1)
    zip_file_name = "sensor_data(" + fileList.front().substring(1, 9) + ").zip";
  else
    zip_file_name = "sensor_data(" + fileList.front().substring(1, 9) + "-" + fileList.back().substring(1, 9) + ").zip";

  // ZIP响应头 (保持不变)
#ifdef WEB_SERVER_ASYNC
  auto response = request->beginResponseStream("application/zip");
  response->addHeader("Content-Disposition", "attachment; filename=" + zip_file_name);
  response->addHeader("Connection", "close");
#else
  server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  server.sendHeader("Content-Type", "application/zip");
  server.sendHeader("Content-Disposition", "attachment; filename=sensor_data.zip");
  server.sendHeader("Connection", "close");
  server.send(200, "application/zip", "");
#endif

  struct CentralDirEntry
  {
    String fileName;
    uint32_t crc;
    uint32_t compressedSize;
    uint32_t uncompressedSize;
    uint32_t localHeaderOffset;
  };
  std::vector<CentralDirEntry> centralDirEntries;
  uint32_t currentOffset = 0;

  int totalFiles = fileList.size();
  int currentFile = 0;
  // 1. 发送本地文件头和数据 (关键修复)
  for (auto &filePath : fileList)
  {
    File f = LittleFS.open(filePath);
    if (!f)
      continue;

    String fileName = f.name();
    size_t fileSize = f.size();

    // 计算CRC32
    CRC32 crc;
    static uint8_t buffer[4096]; // 重复使用
    size_t bytesRead;
    while ((bytesRead = f.read(buffer, sizeof(buffer))) > 0)
    {
      crc.update(buffer, bytesRead);
    }
    f.seek(0); // 重置文件指针

    uint32_t fileCrc = crc.finalize();

    // 记录中央目录信息
    CentralDirEntry entry;
    entry.fileName = fileName;
    entry.crc = fileCrc;
    entry.compressedSize = fileSize;
    entry.uncompressedSize = fileSize;
    entry.localHeaderOffset = currentOffset;
    centralDirEntries.push_back(entry);

    // 更新偏移量
    currentOffset += 30 + fileName.length() + fileSize;

    // 正确的本地文件头结构
    uint8_t localHeader[30] = {
        // 签名 (4)
        0x50, 0x4B, 0x03, 0x04,
        // 版本 (2) - 20 = 2.0
        0x14, 0x00,
        // 通用标记 (2) - 0
        0x00, 0x00,
        // 压缩方法 (2) - 0 = store
        0x00, 0x00,
        // 最后修改时间 (2) - 固定值(11:00)
        0x00, 0x4B,
        // 最后修改日期 (2) - 固定值(2023-01-01)
        0x21, 0x4E,
        // CRC32 (4)
        (uint8_t)(fileCrc), (uint8_t)(fileCrc >> 8),
        (uint8_t)(fileCrc >> 16), (uint8_t)(fileCrc >> 24),
        // 压缩大小 (4)
        (uint8_t)(fileSize), (uint8_t)(fileSize >> 8),
        (uint8_t)(fileSize >> 16), (uint8_t)(fileSize >> 24),
        // 未压缩大小 (4)
        (uint8_t)(fileSize), (uint8_t)(fileSize >> 8),
        (uint8_t)(fileSize >> 16), (uint8_t)(fileSize >> 24),
        // 文件名长度 (2)
        (uint8_t)(fileName.length()), (uint8_t)(fileName.length() >> 8),
        // 扩展字段长度 (2) - 0
        0x00, 0x00};

    // 发送本地文件头
#ifdef WEB_SERVER_ASYNC
    response->write((char *)localHeader, 30);
    response->write(fileName.c_str(), fileName.length());
#else
    server.sendContent((char *)localHeader, 30);
    server.sendContent(fileName.c_str(), fileName.length());
#endif
    // 发送文件内容
    while ((bytesRead = f.read(buffer, sizeof(buffer))) > 0)
    {
#ifdef WEB_SERVER_ASYNC
      response->write((char *)buffer, bytesRead);
#else
      server.sendContent((char *)buffer, bytesRead);
#endif
    }

    LOG_INFO("传输进度: %d/%d - %s", ++currentFile, totalFiles, fileName.c_str());

    f.close();
  }

  // 2. 发送中央目录 (关键修复)
  uint32_t centralDirStart = currentOffset;
  for (auto &entry : centralDirEntries)
  {
    uint16_t nameLength = entry.fileName.length();

    uint8_t centralHeader[46] = {
        // 中央目录签名 (4)
        0x50, 0x4B, 0x01, 0x02,
        // 版本 (2)
        0x14, 0x00,
        // 版本需要 (2)
        0x14, 0x00,
        // 通用标记 (2)
        0x00, 0x00,
        // 压缩方法 (2)
        0x00, 0x00,
        // 最后修改时间 (2)
        0x00, 0x4B,
        // 最后修改日期 (2)
        0x21, 0x4E,
        // CRC32 (4)
        (uint8_t)(entry.crc), (uint8_t)(entry.crc >> 8),
        (uint8_t)(entry.crc >> 16), (uint8_t)(entry.crc >> 24),
        // 压缩大小 (4)
        (uint8_t)(entry.compressedSize), (uint8_t)(entry.compressedSize >> 8),
        (uint8_t)(entry.compressedSize >> 16), (uint8_t)(entry.compressedSize >> 24),
        // 未压缩大小 (4)
        (uint8_t)(entry.uncompressedSize), (uint8_t)(entry.uncompressedSize >> 8),
        (uint8_t)(entry.uncompressedSize >> 16), (uint8_t)(entry.uncompressedSize >> 24),
        // 文件名长度 (2)
        (uint8_t)(nameLength), (uint8_t)(nameLength >> 8),
        // 扩展字段长度 (2)
        0x00, 0x00,
        // 文件注释长度 (2)
        0x00, 0x00,
        // 磁盘号开始 (2)
        0x00, 0x00,
        // 内部文件属性 (2)
        0x00, 0x00,
        // 外部文件属性 (4)
        0x00, 0x00, 0x00, 0x00,
        // 本地文件头偏移 (4)
        (uint8_t)(entry.localHeaderOffset), (uint8_t)(entry.localHeaderOffset >> 8),
        (uint8_t)(entry.localHeaderOffset >> 16), (uint8_t)(entry.localHeaderOffset >> 24)};

#ifdef WEB_SERVER_ASYNC
    response->write((char *)centralHeader, 46);
    response->write(entry.fileName.c_str(), nameLength);
#else
    server.sendContent((char *)centralHeader, 46);
    server.sendContent(entry.fileName.c_str(), nameLength);
#endif
    currentOffset += 46 + nameLength;
  }
  uint32_t centralDirSize = currentOffset - centralDirStart;
  uint16_t numEntries = centralDirEntries.size();

  // 3. 结束中央目录记录 (保持不变)
  uint8_t endRecord[22] = {
      0x50, 0x4B, 0x05, 0x06,                                    // 签名
      0x00, 0x00,                                                // 当前磁盘
      0x00, 0x00,                                                // 中央目录开始磁盘
      (uint8_t)(numEntries), (uint8_t)(numEntries >> 8),         // 本磁盘记录数
      (uint8_t)(numEntries), (uint8_t)(numEntries >> 8),         // 总记录数
      (uint8_t)(centralDirSize), (uint8_t)(centralDirSize >> 8), // 中央目录大小
      (uint8_t)(centralDirSize >> 16), (uint8_t)(centralDirSize >> 24),
      (uint8_t)(centralDirStart), (uint8_t)(centralDirStart >> 8), // 中央目录偏移
      (uint8_t)(centralDirStart >> 16), (uint8_t)(centralDirStart >> 24),
      0x00, 0x00 // 注释长度
  };

#ifdef WEB_SERVER_ASYNC
  response->write((char *)endRecord, 22);
  request->send(response);
  flushAsyncTCP(request);
#else
  server.sendContent((char *)endRecord, 22);
#endif
}

void flushAsyncTCP(AsyncWebServerRequest *request)
{
  // 获取当前连接
  AsyncClient *client = request->client();

  if (client)
  {
    // 强制刷新发送缓冲区
    client->send();

    // 等待所有数据进入发送状态
    while (!client->canSend())
    {
      delay(1);
      yield(); // 维持后台任务
    }

    // 可选：关闭TCP延迟确认
    client->setNoDelay(true);
  }
}

// note：v2版本貌似没有啥变化
#ifdef WEB_SERVER_ASYNC
void handleDownload_v2(AsyncWebServerRequest *request)
#else
void handleDownload_v2()
#endif
{
  // ===== 1. 智能缓冲区管理 =====
  // 使用智能指针自动管理缓冲区
  static auto fileBuffer = std::unique_ptr<uint8_t, void (*)(uint8_t *)>(
      []() -> uint8_t *
      {
        return (uint8_t *)heap_caps_malloc(4096, MALLOC_CAP_DMA);
      }(),
      [](uint8_t *ptr)
      {
        if (ptr)
          heap_caps_free(ptr);
      });

  // 确保缓冲区有效
  if (!fileBuffer)
  {
#ifdef WEB_SERVER_ASYNC
    request->send(500, "text/plain", "Memory allocation failed");
#else
    server.send(500, "text/plain", "Memory allocation failed");
#endif
    return;
  }

  // ===== 2. CRC计算函数 =====
  auto computeCRC32 = [](File f) -> uint32_t
  {
    CRC32 crc;
    uint8_t crcBuffer[512];
    size_t bytesRead;

    while (f.available())
    {
      bytesRead = f.read(crcBuffer, sizeof(crcBuffer));
      if (bytesRead > 0)
        crc.update(crcBuffer, bytesRead);
    }
    return crc.finalize();
  };

  // ===== 3. 文件预处理 =====
  struct FileMeta
  {
    String path;
    String name;
    size_t size;
    uint32_t crc;
    uint32_t headerOffset;
  };

  std::vector<FileMeta> fileMetas;
  uint32_t currentOffset = 0;
  File root = LittleFS.open("/");

  // 收集元数据
  File file = root.openNextFile();
  while (file)
  {
    if (!file.isDirectory() && String(file.name()).endsWith(".csv"))
    {
      FileMeta meta;
      meta.path = file.path();
      meta.name = file.name();
      meta.size = file.size();

      // CRC计算
      File crcFile = LittleFS.open(meta.path, "r");
      if (!crcFile)
        continue;

      meta.crc = computeCRC32(crcFile);
      crcFile.close();

      meta.headerOffset = currentOffset;
      currentOffset += 30 + meta.name.length() + meta.size;

      fileMetas.push_back(meta);
    }
    file = root.openNextFile();
  }
  root.close();

  if (fileMetas.empty())
  {
#ifdef WEB_SERVER_ASYNC
    request->send(404, "text/plain", "no data");
#else
    server.send(404, "text/plain", "no data");
#endif
    return;
  }

  // 生成ZIP文件名
  String zip_file_name;
  if (fileMetas.size() == 1)
  {
    zip_file_name = "sensor_data(" + fileMetas.front().name.substring(0, 8) + ").zip";
  }
  else
  {
    zip_file_name = "sensor_data(" + fileMetas.front().name.substring(0, 8) + "-" +
                    fileMetas.back().name.substring(0, 8) + ").zip";
  }

// ===== 4. 修复变量捕获问题 =====
#ifdef WEB_SERVER_ASYNC
  // 使用局部变量指针避免捕获静态变量
  uint8_t *bufferPtr = fileBuffer.get();

  auto response = request->beginChunkedResponse(
      "application/zip",
      [fileMetas, bufferPtr](uint8_t *buffer, size_t maxLen, size_t index) -> size_t
      {
        // 定义所有状态
        enum TransferState
        {
          SEND_LOCAL_HEADER,
          SEND_FILE_DATA,
          SEND_CENTRAL_DIR,
          SEND_END_RECORD,
          SEND_COMPLETED
        };

        // 状态机变量 (静态保持状态)
        static size_t currentFileIndex = 0;
        static File currentFile;
        static TransferState state = SEND_LOCAL_HEADER;
        static size_t centralDirIndex = 0;

        // 初始化状态机
        if (index == 0)
        {
          currentFileIndex = 0;
          centralDirIndex = 0;
          state = SEND_LOCAL_HEADER;
          currentFile = File();
        }

        size_t bytesGenerated = 0;

        // 状态机处理
        switch (state)
        {
        // 发送本地文件头
        case SEND_LOCAL_HEADER:
        {
          if (currentFileIndex >= fileMetas.size())
          {
            state = SEND_CENTRAL_DIR;
            return 0;
          }

          const auto &meta = fileMetas[currentFileIndex];

          // 构建本地文件头
          uint8_t localHeader[30] = {
              0x50, 0x4B, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
              0x00, 0x4B, 0x21, 0x4E,
              (uint8_t)(meta.crc),
              (uint8_t)(meta.crc >> 8),
              (uint8_t)(meta.crc >> 16),
              (uint8_t)(meta.crc >> 24),
              (uint8_t)(meta.size),
              (uint8_t)(meta.size >> 8),
              (uint8_t)(meta.size >> 16),
              (uint8_t)(meta.size >> 24),
              (uint8_t)(meta.size),
              (uint8_t)(meta.size >> 8),
              (uint8_t)(meta.size >> 16),
              (uint8_t)(meta.size >> 24),
              (uint8_t)(meta.name.length()),
              (uint8_t)(meta.name.length() >> 8),
              0x00, 0x00};

          // 检查缓冲区空间
          size_t headerSize = 30 + meta.name.length();
          if (maxLen < headerSize)
            return 0;

          // 复制头数据
          memcpy(buffer, localHeader, 30);
          memcpy(buffer + 30, meta.name.c_str(), meta.name.length());

          // 打开文件准备发送数据
          currentFile = LittleFS.open(meta.path, "r");
          if (!currentFile)
          {
            currentFileIndex++;
            return 0;
          }

          state = SEND_FILE_DATA;
          bytesGenerated = headerSize;
          break;
        }

        // 发送文件内容
        case SEND_FILE_DATA:
        {
          LOG_INFO("传输进度: %d/%d - %s", currentFileIndex,fileMetas.size(),fileMetas[currentFileIndex].name.c_str());
          
          if (!currentFile || !currentFile.available())
          {
            currentFile.close();
            currentFileIndex++;
            state = SEND_LOCAL_HEADER;
            return 0;
          }

          // 使用局部指针避免捕获问题
          size_t toRead = std::min(maxLen, (size_t)4096);
          toRead = std::min(toRead, currentFile.size() - currentFile.position());

          if (toRead > 0)
          {
            size_t bytesRead = currentFile.read(bufferPtr, toRead);
            memcpy(buffer, bufferPtr, bytesRead);
            bytesGenerated = bytesRead;
          }
          break;
        }

        // 发送中央目录
        case SEND_CENTRAL_DIR:
        {
          if (centralDirIndex >= fileMetas.size())
          {
            state = SEND_END_RECORD;
            return 0;
          }

          const auto &meta = fileMetas[centralDirIndex];
          uint16_t nameLength = meta.name.length();

          // 构建中央目录头
          uint8_t centralHeader[46] = {
              // 签名 (4)
              0x50, 0x4B, 0x01, 0x02,

              // 版本 (4)
              0x14, 0x00, 0x14, 0x00,

              // 通用标记 (2)
              0x00, 0x00,

              // 压缩方法 (2) - 0=存储
              0x00, 0x00,

              // 修改时间 (2) - 00:00:00
              0x00, 0x00,

              // 修改日期 (2) - 1980-01-01
              0x00, 0x00,

              // CRC32 (4)
              (uint8_t)(meta.crc),
              (uint8_t)(meta.crc >> 8),
              (uint8_t)(meta.crc >> 16),
              (uint8_t)(meta.crc >> 24),

              // 压缩大小 (4)
              (uint8_t)(meta.size),
              (uint8_t)(meta.size >> 8),
              (uint8_t)(meta.size >> 16),
              (uint8_t)(meta.size >> 24),

              // 未压缩大小 (4)
              (uint8_t)(meta.size),
              (uint8_t)(meta.size >> 8),
              (uint8_t)(meta.size >> 16),
              (uint8_t)(meta.size >> 24),

              // 文件名长度 (2)
              (uint8_t)(nameLength),
              (uint8_t)(nameLength >> 8),

              // 扩展字段长度 (2)
              0x00, 0x00,

              // 文件注释长度 (2)
              0x00, 0x00,

              // 磁盘号起始 (2)
              0x00, 0x00,

              // 内部文件属性 (2)
              0x00, 0x00,

              // 外部文件属性 (4) - 常规文件
              0x00, 0x00, 0x00, 0x00,

              // 本地文件头偏移量 (4)
              (uint8_t)(meta.headerOffset),
              (uint8_t)(meta.headerOffset >> 8),
              (uint8_t)(meta.headerOffset >> 16),
              (uint8_t)(meta.headerOffset >> 24)};

          size_t headerSize = 46 + nameLength;
          if (maxLen < headerSize)
            return 0;

          memcpy(buffer, centralHeader, 46);
          memcpy(buffer + 46, meta.name.c_str(), nameLength);

          bytesGenerated = headerSize;
          centralDirIndex++;
          break;
        }

        // 发送结束记录
        case SEND_END_RECORD:
        {
          uint16_t numEntries = fileMetas.size();
          uint32_t centralDirSize = 0;
          uint32_t centralDirStart = fileMetas.empty() ? 0 : fileMetas.front().headerOffset;

          for (const auto &meta : fileMetas)
          {
            centralDirSize += 46 + meta.name.length();
          }

          uint8_t endRecord[22] = {
              0x50, 0x4B, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00,
              (uint8_t)(numEntries), (uint8_t)(numEntries >> 8),
              (uint8_t)(numEntries), (uint8_t)(numEntries >> 8),
              (uint8_t)(centralDirSize), (uint8_t)(centralDirSize >> 8),
              (uint8_t)(centralDirSize >> 16), (uint8_t)(centralDirSize >> 24),
              (uint8_t)(centralDirStart), (uint8_t)(centralDirStart >> 8),
              (uint8_t)(centralDirStart >> 16), (uint8_t)(centralDirStart >> 24),
              0x00, 0x00};

          if (maxLen < 22)
            return 0;

          memcpy(buffer, endRecord, 22);
          bytesGenerated = 22;
          state = SEND_COMPLETED;
          break;
        }

        case SEND_COMPLETED:
        default:
          return 0;
        }

        return bytesGenerated;
      });

  // 设置响应头
  response->addHeader("Content-Disposition", "attachment; filename=" + zip_file_name);
  response->addHeader("Connection", "close");

  // ===== 5. 安全发送机制 =====
  // 确保在发送前有足够内存
  if (ESP.getFreeHeap() < 15000)
  {
    request->send(507, "text/plain", "Insufficient Memory");
    return;
  }

  request->send(response);

  // 优化TCP连接
  if (AsyncClient *client = request->client())
  {
    // 通用优化方法
    client->setNoDelay(true);

    // 设置发送超时和重试参数
    client->setAckTimeout(1000);     // ACK超时1秒
    client->setRxTimeout(3);         // 接收超时3秒
    // client->setTxTimeout(3);         // 发送超时3秒
    // client->setMaxPacketsQueued(32); // 最大排队包数

    client->setKeepAlive(10, 3); // 保活探测
    client->send(); // 强制刷
  }
#else
// 同步服务器版本
#endif
}
